package com.techysky.util;

import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.util.ClassUtils;
import org.springframework.util.DigestUtils;


/** * * @author zhangxu * @date 2016年11月11日 上午9:34:18 * @remark 可配置化模块工具 */
public class CoreUtils implements Serializable {
	private static final String GET_CLASS = "getClass";

	private static Log log=LogFactory.getLog(CoreUtils.class);
	
	private static final String SYNCHRID = "synchrid";
	private static final String ENTITY_TYPE = "entityType";
	/** * */
	private static final long serialVersionUID = 1L;

	private CoreUtils() {
	}

	public static CoreUtils getInstance() {
		return new CoreUtils();
	}

	/** * 检查 clazz 是否为简单数据类型 * * */
	public static boolean isSimpleType(Class<?> clazz) {
		return ClassUtils.isPrimitiveOrWrapper(clazz) || ClassUtils.matchesTypeName(String.class, clazz.getName())
				|| ClassUtils.matchesTypeName(Date.class, clazz.getName());
	}

	/** * 控制层转换，针对新增修改处理,只处理非联合主键的 * @param map * @return */
	public static Object map2entity(Map<String, String[]> map) {
		try {
			Class<?> clazz = Class.forName(map.get(ENTITY_TYPE)[0]);
			Object obj = clazz.newInstance();
			for (PropertyDescriptor propertyDescriptor : Introspector.getBeanInfo(clazz).getPropertyDescriptors()) {
				if (map.get(propertyDescriptor.getName()) != null)
					propertyDescriptor.getWriteMethod().invoke(obj,
							str2Object(propertyDescriptor.getPropertyType(),
									null != map.get(propertyDescriptor.getName())
											? map.get(propertyDescriptor.getName())
											: ""));
				else if(null==map.get(propertyDescriptor.getName())&&propertyDescriptor.getName().equals(SYNCHRID)){
						propertyDescriptor.getWriteMethod().invoke(obj,
								UUID.randomUUID().toString().replaceAll("-", ""));
				}
			}
			return obj;
		} catch (Exception e) {
			log.info(e);
		}
		return null;
	}

	public static Object map2Model1(Map<String, Object> map) {
		try {
			Class<?> clazz = Class.forName(map.get(ENTITY_TYPE).toString());
			Object obj = clazz.newInstance();
			for (PropertyDescriptor propertyDescriptor : Introspector.getBeanInfo(clazz).getPropertyDescriptors()) {
				Map<String, Object> booleanMap=booleanMap(map,propertyDescriptor);
				if ((boolean)booleanMap.get("isTrue1"))
					propertyDescriptor.getWriteMethod().invoke(obj,str2Model(propertyDescriptor.getPropertyType(),
									null != map.get(propertyDescriptor.getName())? map.get(propertyDescriptor.getName()): ""));
				else if((boolean)booleanMap.get("isTrue2")){
						propertyDescriptor.getWriteMethod().invoke(obj,UUID.randomUUID().toString().replaceAll("-", ""));
				}
				else if ((boolean)booleanMap.get("isTrue3")) {
							propertyDescriptor.getWriteMethod().invoke(obj,UUID.randomUUID().toString().replaceAll("-", ""));
						}
			}
			return obj;
		} catch (Exception e) {
			log.info(e);
		}
		return null;
	}
	public static Map<String, Object> booleanMap(Map<String, Object> map,PropertyDescriptor propertyDescriptor){
		Map<String, Object> booleanMap=new HashMap<>();
		boolean isTrue1= map.get(propertyDescriptor.getName()) != null&& !"".equals(map.get(propertyDescriptor.getName()).toString());
		boolean isTrue2=!isTrue1&&"id".equals(propertyDescriptor.getName());
		boolean isTrue3=!isTrue1&&map.get("operationType") != null&&propertyDescriptor.getName().equals(SYNCHRID);
		booleanMap.put("isTrue1", isTrue1);
		booleanMap.put("isTrue2", isTrue2);
		booleanMap.put("isTrue3", isTrue3);
		return booleanMap;
	}

	public static Object map2Model(Map<String, Object> map) {
		try {
			Class<?> clazz = Class.forName(map.get(ENTITY_TYPE).toString());
			Object obj = clazz.newInstance();
			for (PropertyDescriptor propertyDescriptor : Introspector.getBeanInfo(clazz).getPropertyDescriptors()) {
				boolean isTrue=map.get(propertyDescriptor.getName()) != null&& !"".equals(map.get(propertyDescriptor.getName()).toString());
				boolean isTrue2=!isTrue&&map.get("operationType") != null&&propertyDescriptor.getName().equals(SYNCHRID);
				if (isTrue)
					propertyDescriptor.getWriteMethod().invoke(obj,
							str2Model(propertyDescriptor.getPropertyType(),
									null != map.get(propertyDescriptor.getName())? map.get(propertyDescriptor.getName()): ""));
				else if (isTrue2) {
							propertyDescriptor.getWriteMethod().invoke(obj,
									UUID.randomUUID().toString().replaceAll("-", ""));
					}
			}
			return obj;
		} catch (Exception e) {
			log.info(e);
		}
		return null;
	}

	/** * 查询 * @param map * @return */
	public static Object map2entityQ(Map<String, String[]> map) {
		try {
			Class<?> clazz = Class.forName(map.get(ENTITY_TYPE)[0]);
			Object obj = clazz.newInstance();
			for (PropertyDescriptor propertyDescriptor : Introspector.getBeanInfo(clazz).getPropertyDescriptors()) {
				if (map.get(propertyDescriptor.getName()) != null)
					propertyDescriptor.getWriteMethod().invoke(obj,
							str2Object(propertyDescriptor.getPropertyType(),
									null != map.get(propertyDescriptor.getName())
											? map.get(propertyDescriptor.getName())
											: ""));
			}
			return obj;
		} catch (Exception e) {
			log.info(e);
		}
		return null;
	}

	private static Object str2Object1(Class<?> type, Object obj) {
		Object param = null;
		if (null != obj && !"".equals(obj.toString())) {
			param = strTran(type, obj, param);
		}
		return param;
	}

	private static Object strTran(Class<?> type, Object obj, Object param1) {
		Object param=param1;
		String v = obj.toString();
		if (ClassUtils.matchesTypeName(String.class, type.getName()))
			param = String.valueOf(v);
		else if (ClassUtils.isAssignable(Integer.class, type))
			param = Integer.valueOf(v);
		else if (ClassUtils.isAssignable(Float.class, type))
			param = Float.valueOf(v);
		else if (ClassUtils.isAssignable(Double.class, type))
			param = Double.valueOf(v);
		else if (ClassUtils.isAssignable(Short.class, type))
			param = Short.valueOf(v);
		else if (ClassUtils.isAssignable(Boolean.class, type))
			param = Boolean.valueOf(v);
		else if (ClassUtils.isAssignable(Long.class, type))
			param = Long.valueOf(v);
		else if (ClassUtils.isAssignable(Byte.class, type))
			param = Byte.valueOf(v);
		else if (ClassUtils.isAssignable(Character.class, type))
			param = v.charAt(0);
		return param;
	}

	public static Object map2EntityClazz(Map<String, Object> map, Class<?> clazz) {
		try {
			Object obj = clazz.newInstance();
			for (PropertyDescriptor propertyDescriptor : Introspector.getBeanInfo(clazz).getPropertyDescriptors()) {
				if (map.get(propertyDescriptor.getName()) != null)
					propertyDescriptor.getWriteMethod().invoke(obj,
							str2Object1(propertyDescriptor.getPropertyType(),
									null != map.get(propertyDescriptor.getName())
											? map.get(propertyDescriptor.getName())
											: ""));
			}
			return obj;
		} catch (Exception e) {
			log.info(e);
		}
		return null;
	}

	/** * 控制层转换，针对新增修改处理,只处理联合主键的 * @param map * @return */
	public static Object map2entityCombine(Map<String, String[]> map) {
		try {
			Class<?> clazz = Class.forName(map.get(ENTITY_TYPE)[0]);
			Object obj = clazz.newInstance();
			for (PropertyDescriptor propertyDescriptor : Introspector.getBeanInfo(clazz).getPropertyDescriptors()) {
				if (map.get(propertyDescriptor.getName()) != null)
					propertyDescriptor.getWriteMethod().invoke(obj,
							str2Object(propertyDescriptor.getPropertyType(),
									null != map.get(propertyDescriptor.getName())
											? map.get(propertyDescriptor.getName())
											: ""));
			}
			return convertCombine(obj);
		} catch (Exception e) {
			log.info(e);
		}
		return null;
	}

	public static Object convertCombine(Object obj) {
		Class<?> clz = obj.getClass();
		try {
			Field[] fields = clz.getDeclaredFields();
			for (Field field : fields){
				extract2(obj, field);
			}
				
		} catch (Exception e) {
			log.info(e);
		}
		return obj;
	}

	private static void extract2(Object obj, Field field) throws IllegalAccessException {
		if (field.getName().equals(SYNCHRID)) {
			field.setAccessible(true);
			if (field.get(obj) == null) {
				field.set(obj, UUID.randomUUID().toString().replace("-", ""));
			}
		}
	}

	public static Object getSynchrid(Object obj) {
		Class<?> clz = obj.getClass();
		try {
			Field[] fields = clz.getDeclaredFields();
			for (Field field : fields)
				if (field.getName().equals(SYNCHRID)) {
					field.setAccessible(true);
					return field.get(obj);
				}
		} catch (Exception e) {
			log.info(e);
		}
		return null;
	}

	public static Object convertCombineclx(Object obj) {
		Class<?> clz = obj.getClass();
		try {
			Field[] fields = clz.getDeclaredFields();
			for (Field field : fields)
				if (field.getName().equals(SYNCHRID)) {
					field.setAccessible(true);
					field.set(obj, UUID.randomUUID().toString().replace("-", ""));
					break;
				}
		} catch (Exception e) {
			log.info(e);
		}
		return obj;
	}

	/** * 获取服务对应的实体全名 * @param model * @return */
	public String getModel(String model) {
		return model.contains(".") ? model
				: "com.techysky.entity." + model.substring(0, 1).toUpperCase() + model.substring(1, model.length());
	}

	/** * 将List结果集合转成Map * @return */
	@SuppressWarnings("rawtypes")
	public List<Map<String, Object>> convert(List results) {
		List<Map<String, Object>> result = new ArrayList<>();
		if (!results.isEmpty())
			for (Object obj : results) {
				result.add(entity2Map(obj));
			}
		return result;
	}

	public Object getObj(String model) {
		try {
			return this.getClass(model).newInstance();
		} catch (Exception e) {
			log.info(e);
		}
		return null;
	}

	/** * 该方案可以将共用的字段抽出来 * @param obj * @return */
	public Map<String, Object> entity2Map(Object obj) {
		Map<String, Object> map = new LinkedHashMap<>();
		try {
			Method[] methods = obj.getClass().getMethods();
			for (Method method : methods) {
				if (method.getName().startsWith("get") && !GET_CLASS.equals(method.getName())
						&& isSimpleType(method.getReturnType())) {
					String key = method.getName().substring(3).substring(0, 1).toLowerCase()
							+ method.getName().substring(3).substring(1);
					Object value = method.invoke(obj);
					convertProp(map, method, key, value);
				}
			}
		} catch (Exception e) {
			log.info(e);
		}
		return map;
	}

	public Object convertEntity(Object obj) {
		try {
			Method[] methods = obj.getClass().getMethods();
			for (Method method : methods) {
				boolean isTrue= method.getName().startsWith("get") && !GET_CLASS.equals(method.getName());
				boolean isTrue1=isTrue&&"getId".equals(method.getName()) && method.invoke(obj) == null;
				boolean isTrue2=isTrue&&"getPassword".equals(method.getName())&& obj.getClass().getMethod("getId").invoke(obj) != null;
					if (isTrue1) {
						Method id = obj.getClass().getMethod("set" + method.getName().substring(3), String.class);
						id.invoke(obj, UUID.randomUUID().toString().replaceAll("-", ""));
					} else if (isTrue2)
							 {
						method.invoke(obj, DigestUtils.md5DigestAsHex(method.invoke(obj).toString().getBytes()));
					}
			}
		} catch (Exception e) {
			log.info(e);
		}
		return obj;
	}

	public String coreKey(Object obj) {
		StringBuilder sb = new StringBuilder();
		try {
			Method[] methods = obj.getClass().getMethods();
			int i = 0;
			for (Method method : methods) {
				i = extract3(obj, sb, i, method);
			}
		} catch (Exception e) {
			log.info(e);
		}
		return sb.toString();
	}

	private int extract3(Object obj, StringBuilder sb, int i1, Method method)
			throws IllegalAccessException, InvocationTargetException {
		int i=i1;
		i++;
		if (i < 11 && method.getName().startsWith("get") && !GET_CLASS.equals(method.getName())
				&& isSimpleType(method.getReturnType())) {
			Object value = method.invoke(obj);
			if (value != null && !"".equals(value))
				sb.append(value.toString()).append(".");
		}
		return i;
	}

	/** * * @param map * @param method * @param key * @param value */
	private void convertProp(Map<String, Object> map, Method method, String key, Object value) {
		if (value != null && !"".equals(value.toString())) {
			if (method.getReturnType() == Date.class)
				map.put(key, date2str(value));
			else
				map.put(key, value);
		} else {
			map.put(key, "");
		}
	}

	/** * * @param obj * @return */
	public String date2str(Object obj) {
		if (null != obj) {
			SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
			return sdf.format(obj);
		} else
			return "";
	}

	/** * * @param model * @return */
	public Class<?> getClass(String model) {
		try {
			return Class.forName(getModel(model));
		} catch (ClassNotFoundException e) {
			log.info(e);
		}
		return null;
	}

	/** * * @param obj * @return */
	public Object assign2Entity(Object obj) {
		try {
			Method[] methods = obj.getClass().getMethods();
			for (Method method : methods) {
				if (method.getName().startsWith("set")) {
					assign2prop(obj, method);
				}
			}
		} catch (Exception e) {
			log.info(e);
		}
		return obj;
	}

	/**
	 * * * @param obj * @param method * @throws IllegalAccessException * @throws
	 * InvocationTargetException
	 */
	private void assign2prop(Object obj, Method method) throws IllegalAccessException, InvocationTargetException {
		String key = method.getName().substring(3).substring(0, 1).toLowerCase()
				+ method.getName().substring(3).substring(1);
		if (ENTITY_TYPE.equals(key))
			method.invoke(obj, obj.getClass().getName());
		else if ("createDate".equals(key))
			method.invoke(obj, Calendar.getInstance().getTime());
		else if ("orderCode".equals(key))
			method.invoke(obj, Calendar.getInstance().getTimeInMillis());
	}

	/**
	 * String -> Any，如果 handler 为 null 则把字符串转换为 8 种基础数据类型、及其包装类、 {@link Date} 或
	 * {@link String}， * 如果 handler 不为 null 则由 handler 执行转换 * * @param type : 目标类型的
	 * {@link Class} 对象 * @param v : 要转换的字符串 * @param handler : 类型转换处理器 * @return :
	 * 转换结果，如果转换不成功返回 null * @throws : 如果目标类型不支持抛出 {@link IllegalArgumentException}
	 * *
	 */
	public static final Object str2Object(Class<?> type, Object obj) {
		String[] results = (String[]) obj;
		Object param = null;
		if (!"".equals(results[0])) {
			String v = results[0];
			if (type == String.class)
				{param = String.valueOf(v);}
			else if (type == int.class || Integer.class == type)
				{param = Integer.valueOf(v);}
			else if (type == float.class || type == Float.class)
				{param = Float.valueOf(v);}
			else if (type == double.class || type == Double.class)
				{param = Double.valueOf(v);}
			else{
			param = extract6(type, param, v);
		}
		}
		return param;
	}

	private static Object extract6(Class<?> type, Object param1, String v) {
		Object param=param1;
		if (type == short.class || type == Short.class)
			{param = Short.valueOf(v);}
		else if (type == boolean.class || type == Boolean.class)
			{param = Boolean.valueOf(v);}
		else if (type == long.class || type == Long.class)
			{param = Long.valueOf(v);}
		else {
			param = extract7(type, v, param);
		}
		return param;
	}

	private static Object extract7(Class<?> type, String v, Object param1) {
		Object param=param1;
		if (type == byte.class || type == Byte.class)
		{param = Byte.valueOf(v);}
		else if (type == char.class || type == Character.class)
		{param = v.charAt(0);}
		return param;
	}

	public static final Object str2Model(Class<?> type, Object obj) {
		String results = String.valueOf(obj);
		Object param = null;
		if (results != null && !"".equals(results) && !"null".equals(results)) {
			String v = results;
			if (type == String.class)
				param = String.valueOf(v);
			else if (type == int.class || Integer.class == type)
				param = Integer.valueOf(v);
			else if (type == float.class || type == Float.class)
				param = Float.valueOf(v);
			else{
			param = extract4(type, param, v);
		}
		}
		return param;
	}

	private static Object extract4(Class<?> type, Object param1, String v) {
		Object param=param1;
		if (type == double.class || type == Double.class)
			param = Double.valueOf(v);
		else if (type == short.class || type == Short.class)
			param = Short.valueOf(v);
		else if (type == boolean.class || type == Boolean.class)
			param = Boolean.valueOf(v);
		else{
		param = extract5(type, v, param);
		}
		return param;
	}

	private static Object extract5(Class<?> type, String v, Object param1) {
		Object param=param1;
		if (type == long.class || type == Long.class)
			param = Long.valueOf(v);
		else if (type == byte.class || type == Byte.class)
			param = Byte.valueOf(v);
		else if (type == char.class || type == Character.class)
			param = v.charAt(0);
		return param;
	}
}